'''
gameroot
Author: Alex Foran
'''

import pygame,loaders,utility,os,sys,platform,titlescreen,monitors,audiosystem,pane,gamelogic,threading

#static values for testing/production changes
respath="../resources/"
gametitle="pewpewpew"
gameversion=0.5
prefsfile=respath+"prefs.xml"

'''
Class signifying the root of the game.

The root should contain basic peripheral modules, oft-used resources, and a single root pane, through which every game object can either be accessed or passed. When this pane is done, the game is done.
'''
class GameRoot:
	'''
	Constructor.
	'''
	def __init__(self):
		#init game values
		pygame.init()
		self.gameclock=pygame.time.Clock()
		self.graphicsclock=pygame.time.Clock()
		self.monitor=monitors.InputMonitor()
		self.audiosys=audiosystem.Audiosystem()
		pygame.mouse.set_visible(False)
		
		#loaders
		self.loadprefs()
		self.showdisplay()
		self.loadcommon()
		
		#load initial pane
		self.pane=titlescreen.TitleScreen(self)
		
		#core game loops
		GameGraphics(self).start() # run graphics on a separate thread in case the system sucks
		self.logicloop() # update methods need to be on the main thread so that monitors get handled correctly... pygame is silly
	
	'''
	Load preferences from file, or just uses defaults if an error is encountered.
	'''
	def loadprefs(self):
		#load preference dict - some of these are invisible to the user unless they check the xml
		self.prefs={}
		self.prefs["resolution"]=(640,480)
		self.prefs["fullscreen"]=False
		self.prefs["bgmvolume"]=0.7
		self.prefs["sfxvolume"]=0.7
		self.prefs["usealphablending"]=True
		self.prefs["showcursor"]=False
		self.prefs["joysticktolerance"]=0.5
		self.prefs["key_thrust"]="W"
		self.prefs["key_stabilize"]="S"
		self.prefs["key_rotateleft"]="A"
		self.prefs["key_rotateright"]="D"
		self.prefs["key_mainfire"]="SPACE"
		self.prefs["key_altfire"]="MOUSE2"
		self.prefs["key_altdown"]="MOUSE4"
		self.prefs["key_altup"]="MOUSE5"
		self.prefs["key_snap1"]="1"
		self.prefs["key_snap2"]="2"
		self.prefs["key_snap3"]="3"
		self.prefs["key_snap4"]="4"
		self.prefs["key_snap5"]="5"
		self.prefs["key_snap6"]="6"
		self.prefs["key_snap7"]="7"
		self.prefs["key_snap8"]="8"
		self.prefs["key_up"]="UP"
		self.prefs["key_down"]="DOWN"
		self.prefs["key_left"]="LEFT"
		self.prefs["key_right"]="RIGHT"
		self.prefs["key_accept"]="RETURN"
		self.prefs["key_cancel"]="ESCAPE"
		self.prefs["key_shoot"]="SPACE"
		#parse the preferences after creating defaults
		self.parseprefs(utility.loadXML(prefsfile))
	
	def parseprefs(self,xml):
		#error cases uno and dos
		if xml==None or xml.xml==None:
			print "Could not load preferences. Loading defaults."
		else:
			#parse all the special options normally
			options=xml.getElementsByTagName("option")
			for option in options:
				if option.attributes["name"].value=="width":
					#it's a tuple
					self.prefs["resolution"]=(int(option.attributes["value"].value),self.prefs["resolution"][1])
				elif option.attributes["name"].value=="height":
					#it's a tuple
					self.prefs["resolution"]=(self.prefs["resolution"][0],int(option.attributes["value"].value))
				elif option.attributes["name"].value=="fullscreen":
					self.prefs["fullscreen"]=(option.attributes["value"].value=="true")
				elif option.attributes["name"].value=="bgmvolume":
					self.prefs["bgmvolume"]=float(option.attributes["value"].value)
				elif option.attributes["name"].value=="sfxvolume":
					self.prefs["sfxvolume"]=float(option.attributes["value"].value)
				elif option.attributes["name"].value=="joysticktolerance":
					self.prefs["joysticktolerance"]=float(option.attributes["value"].value)
				elif option.attributes["name"].value=="usealphablending":
					self.prefs["usealphablending"]=(option.attributes["value"].value=="true")
				elif option.attributes["name"].value=="showcursor":
					self.prefs["showcursor"]=(option.attributes["value"].value=="true")
				else:
					#simple string parsing
					self.prefs[option.attributes["name"].value]=option.attributes["value"].value
	
	'''
	Saves the current prefs object into the designated file. Any defaults not yet overwritten will be included.
	'''
	def saveprefs(self):
		xml=utility.XML(prefsfile,False)
		#1.0 is a fine doctype
		text='<?xml version="1.0" ?>\n'
		text+='<prefs>\n'
		#just write 'em all out
		for key in self.prefs:
			if key=="resolution":
				text+='\t<option name="width" value="'+str(self.prefs["resolution"][0])+'"/>\n'
				text+='\t<option name="height" value="'+str(self.prefs["resolution"][1])+'"/>\n'
			elif key=="fullscreen" or key=="usealphablending" or key=="showcursor":
				text+='\t<option name="'+key+'" value="'+("true" if self.prefs[key] else "false")+'"/>\n'
			else:
				text+='\t<option name="'+key+'" value="'+str(self.prefs[key])+'"/>\n'
		text+='</prefs>\n'
		#save it
		xml.save(text)
	
	'''
	Shows the display, configured according to prefs.
	'''
	def showdisplay(self):
		#init display
		#windows fix - directx has problems
		if platform.system() == 'Windows':
			os.environ['SDL_VIDEODRIVER'] = 'windib'
		pygame.display.set_caption(gametitle)
		if self.prefs["fullscreen"]:
			pygame.display.set_mode(self.prefs["resolution"],pygame.FULLSCREEN)
		else:
			pygame.display.set_mode(self.prefs["resolution"],pygame.DOUBLEBUF)
		pygame.display.set_icon(loaders.load_image(respath+"icon.png"))
		
	'''
	Loads commonly-used resources. This includes fonts and cursors.
	'''
	def loadcommon(self):
		#various heavily-used resources
		self.cursor=pane.MouseCursor(loaders.load_image_alpha(respath+"dialogs/cursor-shadow.png"),(-9,-8))
		self.font18=pygame.font.Font(respath+"fonts/alexforan.ttf",18)
		self.font24=pygame.font.Font(respath+"fonts/alexforan.ttf",24)
		self.font28=pygame.font.Font(respath+"fonts/alexforan.ttf",28)
		self.font36=pygame.font.Font(respath+"fonts/alexforan.ttf",36)
		self.font48=pygame.font.Font(respath+"fonts/alexforan.ttf",48)
	
	'''
	Core logic loop. Simply calls the update() method of the pane... everything should flow from there.
	'''
	def logicloop(self):
		#run until the pane is done
		while not self.pane.done:
			#handle events
			events=pygame.event.get()
			for event in events:
				#if the program wants to quit, signal all threads to quit
				if event.type==pygame.QUIT:
					self.pane.done=True
				else:
					#let the monitor process the event
					self.monitor.process_event(event,self.prefs["joysticktolerance"])
			#update the pane... this is where it all gets done
			self.pane.update()
			#clear monitors states
			self.monitor.update()
			#update a maximum of 100 times per second
			self.gameclock.tick(100)
	
	'''
	Core graphics loop. Simple calls the repaint() method of the pane.
	'''
	def graphicsloop(self):
		while not self.pane.done:
			#repaint
			self.pane.repaint()
			#actually show that on-screen
			pygame.display.flip()
			#cap at 60 frames per second
			self.graphicsclock.tick(60)

'''
Thread class to run the graphics loop.
'''
class GameGraphics (threading.Thread):
	def __init__(self,root):
		threading.Thread.__init__(self)
		self.root=root
		
	def run(self):
		self.root.graphicsloop()